package v1

import (
	"context"
	"fmt"
	"github.com/gin-gonic/gin"
	excelize "github.com/xuri/excelize/v2"
	"go-micro.dev/v4/util/log"
	"math/rand"
	"maya-service/bll"
	"maya-service/model"
	"maya-service/model/po"
	"maya-service/server/web/middleware"
	"maya-service/utils"
	"os"
	"strconv"
	"time"
)

var Question = &question{}

func init() {
	RegisterRouter(Question)
}

type question struct{}

// Init
func (a *question) Init(r *gin.RouterGroup) {
	g := r.Group("/question", middleware.Auth())
	{
		g.POST("/create", a.create)
		g.POST("/update", a.update)
		g.POST("/list", a.list)
		g.POST("/delete", a.delete)
		g.POST("/detail", a.find)
		g.POST("/batchDelete", a.batchDelete)
		// 根据关卡获取问题数据
		g.POST("/listByLevel", a.listByLevel)
		// 问题导入
		g.POST("/import", a.importQuestion)

	}
}

// create
func (a *question) create(c *gin.Context) {
	var (
		in  = &model.QuestionCreateRequest{}
		err error
	)

	if err = c.ShouldBindJSON(in); err != nil {
		c.Error(err)
		return
	}

	if err = bll.Question.Create(c.Request.Context(), in); err != nil {
		c.Error(err)
		return
	}
	utils.ResponseOk(c, nil)
}

// update
func (a *question) update(c *gin.Context) {
	var (
		in  = &model.QuestionUpdateRequest{}
		err error
	)

	if err = c.ShouldBindJSON(in); err != nil {
		c.Error(err)
		return
	}

	if err = bll.Question.Update(c.Request.Context(), in); err != nil {
		c.Error(err)
		return
	}
	utils.ResponseOk(c, nil)
}

// list
func (a *question) list(c *gin.Context) {
	var (
		in  = &model.QuestionListRequest{}
		out = &model.QuestionListResponse{}
		err error
	)

	if err = c.ShouldBindJSON(in); err != nil {
		c.Error(err)
		return
	}

	if out, err = bll.Question.List(c.Request.Context(), in); err != nil {
		c.Error(err)
		return
	}
	utils.ResponseOk(c, out)
}

// list
func (a *question) find(c *gin.Context) {
	var (
		in  = &model.QuestionInfoRequest{}
		out = &model.QuestionInfo{}
		err error
	)

	if err = c.ShouldBindJSON(in); err != nil {
		c.Error(err)
		return
	}

	if out, err = bll.Question.Find(c.Request.Context(), in); err != nil {
		c.Error(err)
		return
	}
	utils.ResponseOk(c, out)
}

// delete
func (a *question) delete(c *gin.Context) {
	var (
		in  = &model.QuestionDeleteRequest{}
		err error
	)

	if err = c.ShouldBindJSON(in); err != nil {
		c.Error(err)
		return
	}

	if err = bll.Question.Delete(c.Request.Context(), in); err != nil {
		c.Error(err)
		return
	}

	// 删除选项
	err = bll.Option.Delete(c.Request.Context(), &model.OptionDeleteRequest{
		TitleId: in.Id,
	})
	if err != nil {
		c.Error(err)
		return
	}
	utils.ResponseOk(c, nil)
}

// batchDelete 批量删除
func (a *question) batchDelete(c *gin.Context) {
	var (
		in  = &po.BatchDeleteReq{}
		err error
	)

	if err = c.ShouldBindJSON(in); err != nil {
		c.Error(err)
		return
	}

	// 一键删除
	if in.LevelId > 0 {
		list, _ := bll.Question.List(c.Request.Context(), &model.QuestionListRequest{
			LevelId: &in.LevelId,
		})

		var titleIds []int64
		for i := 0; i < len(list.List); i++ {
			titleIds = append(titleIds, list.List[i].Id)
		}

		// 删除选项
		err = bll.Option.Delete(c.Request.Context(), &model.OptionDeleteRequest{
			TitleIds: titleIds,
		})
		if err != nil {
			c.Error(err)
			return
		}

		// 删除问题
		err = bll.Question.Delete(c.Request.Context(), &model.QuestionDeleteRequest{
			LevelId: in.LevelId,
		})
		if err != nil {
			c.Error(err)
			return
		}
		// 回复数据
		utils.ResponseOk(c, nil)
		return
	}

	// 批量删除
	if len(in.QuestionIds) > 0 {
		// 删除选项
		err = bll.Option.Delete(c.Request.Context(), &model.OptionDeleteRequest{
			TitleIds: in.QuestionIds,
		})
		if err != nil {
			c.Error(err)
			return
		}

		// 删除问题
		err = bll.Question.Delete(c.Request.Context(), &model.QuestionDeleteRequest{
			Ids: in.QuestionIds,
		})
		if err != nil {
			c.Error(err)
			return
		}
		// 回复数据
		utils.ResponseOk(c, nil)
		return
	}

	c.Error(fmt.Errorf("请根据条件进行删除"))
	return
}

// listByLevel 根据关卡获取问题数据
func (a *question) listByLevel(c *gin.Context) {
	// 计算大关、小关对应的题目数量范围（普通题目、问答题目），随机找出当前关对应的题目数量
	var (
		in  = &po.QuestionReq{}
		err error
	)

	if err = c.ShouldBindJSON(in); err != nil {
		c.Error(err)
		return
	}

	// 获取关卡数据
	var gameLevelInfo *model.GameLevelInfo
	gameLevelInfo, err = bll.GameLevel.Find(c.Request.Context(), &model.GameLevelInfoRequest{
		Weight:   int(in.SecondLevel),
		ParentId: int(in.FirstLevel),
	})
	if err != nil {
		c.Error(err)
		return
	}

	// 查询出当前关卡下所有问题数据
	var questionListRet *model.QuestionListResponse
	questionListRet, err = bll.Question.List(c.Request.Context(), &model.QuestionListRequest{
		LevelId: &gameLevelInfo.Id,
		Index:   0,
		Size:    100,
	})
	if err != nil {
		c.Error(err)
		return
	}

	var normalTitleIds []int64
	var essayQuestionTitleIds []int64
	questionMap := make(map[int64]*model.QuestionInfo)
	for i := 0; i < len(questionListRet.List); i++ {
		questionMap[questionListRet.List[i].Id] = questionListRet.List[i]
		// 类型 0 文字 1 图片 2 问答题
		if questionListRet.List[i].Type != 2 {
			normalTitleIds = append(normalTitleIds, questionListRet.List[i].Id)
		} else {
			essayQuestionTitleIds = append(essayQuestionTitleIds, questionListRet.List[i].Id)
		}
	}

	// 利用随机数算法获取相应的数据
	var randomTitleIds []int64
	questionRsp := &po.QuestionRsp{}
	// 随机出普通题目数量
	normalTitleCount := gameLevelInfo.QuestionCount - gameLevelInfo.EssayQuestionCount
	if len(normalTitleIds) < int(normalTitleCount) {
		normalTitleCount = int64(len(normalTitleIds))
	}
	if normalTitleCount > 0 {
		randomNormalTitleIds := SelectRandomSet(normalTitleIds, int(normalTitleCount))
		randomTitleIds = append(randomTitleIds, randomNormalTitleIds...)
		for i := 0; i < len(randomNormalTitleIds); i++ {
			if v, ok := questionMap[randomNormalTitleIds[i]]; ok {
				questionRsp.Questions = append(questionRsp.Questions, &po.Question{
					TitleId:   randomNormalTitleIds[i],
					Title:     v.Title,
					TitleType: v.Type,
					Image:     v.Image,
					AnswerId:  []int64{v.AnswerId},
				})
			}
		}
	}

	// 随机出相应数量的问答题数据
	if gameLevelInfo.EssayQuestionCount > 0 && len(essayQuestionTitleIds) > 0 {
		randomEssayQuestionIds := SelectRandomSet(essayQuestionTitleIds, int(gameLevelInfo.EssayQuestionCount))
		randomTitleIds = append(randomTitleIds, randomEssayQuestionIds...)
		for i := 0; i < len(randomEssayQuestionIds); i++ {
			if v, ok := questionMap[randomEssayQuestionIds[i]]; ok {
				questionRsp.Questions = append(questionRsp.Questions, &po.Question{
					TitleId:   randomEssayQuestionIds[i],
					Title:     v.Title,
					TitleType: v.Type,
					Image:     v.Image,
					AnswerId:  []int64{v.AnswerId},
				})
			}
		}
	}

	var optionListRet *model.OptionListResponse
	if len(randomTitleIds) > 0 {
		optionListRet, err = bll.Option.List(c.Request.Context(), &model.OptionListRequest{
			TitleIds: randomTitleIds,
		})
		if err != nil {
			c.Error(err)
			return
		}
	}

	// 构建选项数据
	optionMap := make(map[int64][]*model.OptionInfo)
	if optionListRet != nil {
		for i := 0; i < len(optionListRet.List); i++ {
			optionMap[optionListRet.List[i].TitleId] = append(optionMap[optionListRet.List[i].TitleId], optionListRet.List[i])
		}
	}

	// 组装结果
	for i := 0; i < len(questionRsp.Questions); i++ {
		questionRsp.Questions[i].Options = HandlerOption(questionRsp.Questions[i].TitleType, optionMap[questionRsp.Questions[i].TitleId], questionRsp.Questions[i].AnswerId[0], in.Difficulty)
	}

	questionRsp.Questions = Random(questionRsp.Questions)
	// 回复数据
	utils.ResponseOk(c, questionRsp)
}

// HandlerOption 处理选项结果
// 类型 0 文字 1 图片 2 问答题
func HandlerOption(questionType int, options []*model.OptionInfo, answerId int64, difficulty int) (ret []*model.OptionInfo) {
	// 排除准确答案
	var normalOptionIds []int64
	optionsMap := make(map[int64]*model.OptionInfo)
	for i := 0; i < len(options); i++ {
		optionsMap[options[i].Id] = options[i]
		if options[i].Id == answerId {
			continue
		}
		normalOptionIds = append(normalOptionIds, options[i].Id)
	}

	// 问答题直接返回结果
	if questionType == 2 {
		if v, ok := optionsMap[answerId]; ok {
			ret = append(ret, v)
		}
		return
	}

	var randResult []int64
	switch difficulty {
	// 新手
	case 1:
		randResult = SelectRandomSet(normalOptionIds, 1)
		// 高手
	case 2:
		randResult = SelectRandomSet(normalOptionIds, 2)
	default:
		randResult = SelectRandomSet(normalOptionIds, 3)
	}
	randResult = append(randResult, answerId)

	// 打乱顺序
	shuffleArray(randResult)
	for i := 0; i < len(randResult); i++ {
		if v, ok := optionsMap[randResult[i]]; ok {
			ret = append(ret, v)
		}
	}
	return
}

type FileTypeSuffix string

const (
	FileXls  FileTypeSuffix = "xls"
	FileXlsx FileTypeSuffix = "xlsx"
)

var FileMap = map[string]FileTypeSuffix{
	"xls":  FileXls,
	"xlsx": FileXlsx,
}

// importQuestion 导入问题数据
func (a *question) importQuestion(c *gin.Context) {
	// 保存文件
	filePath, err := UploadFile(c)
	if err != nil {
		c.Error(err)
		return
	}

	defer func() {
		os.RemoveAll(filePath)
	}()

	// 读取数据
	var file *excelize.File
	defer func() {
		if err = file.Close(); err != nil {
			log.Error(err)
		}
	}()

	// 读取表格内容
	if file, err = excelize.OpenFile(filePath); err != nil {
		c.Error(fmt.Errorf("文件解析失败"))
		return
	}

	sheets := file.GetSheetMap()

	// 获取工作表中指定单元格的值
	rows, err := file.GetRows(sheets[1])
	if err != nil {
		c.Error(fmt.Errorf("excel 格式解析错误"))
		return
	}

	// 导入关卡
	importGameLevel(rows)

	// 导入问题
	rows, err = file.GetRows(sheets[2])
	if err != nil {
		c.Error(fmt.Errorf("excel 格式解析错误"))
		return
	}
	importQuestion(rows)
	// 导入答案
	rows, err = file.GetRows(sheets[3])
	if err != nil {
		c.Error(fmt.Errorf("excel 格式解析错误"))
		return
	}
	importOption(rows)
	utils.ResponseOk(c, nil)
}

// SelectRandomSet 从 arr 中随机选择 N 个不重复的整数
func SelectRandomSet(arr []int64, N int) []int64 {
	if N > len(arr) {
		N = len(arr)
	}

	// 初始化随机数生成器
	rand.Seed(time.Now().UnixNano())

	// 结果集
	result := make([]int64, N)
	// 已选择的索引集
	chosen := make(map[int]bool)
	for i := 0; i < N; {
		index := rand.Intn(len(arr))
		if !chosen[index] {
			result[i] = arr[index]
			i++
			chosen[index] = true
		}
	}
	return result
}

// 数组打乱顺序
func shuffleArray(arr []int64) {
	rand.Seed(time.Now().UnixNano())
	for i := len(arr) - 1; i > 0; i-- {
		j := rand.Intn(i + 1)
		arr[i], arr[j] = arr[j], arr[i]
	}
}

func Random(obj []*po.Question) []*po.Question { //interface
	rand.Seed(time.Now().UnixNano())
	for i := len(obj) - 1; i > 0; i-- {
		num := rand.Intn(i + 1)
		obj[i], obj[num] = obj[num], obj[i]
	}

	new_obj := make([]*po.Question, 0)
	for i := 0; i < len(obj); i++ {
		new_obj = append(new_obj, obj[i])
	}
	return new_obj
}

// 关卡导入
func importGameLevel(rows [][]string) (err error) {
	for index, row := range rows {
		if index == 0 {
			continue
		}

		// 插入关卡数据
		err = bll.GameLevel.Create(context.Background(), &model.GameLevelCreateRequest{
			Desc:               GetData(row, 0),
			Weight:             int(StrToInt64(GetData(row, 1))),
			Image:              GetImageUrl(GetData(row, 2)),
			Type:               int(StrToInt64(GetData(row, 3))),
			ParentId:           StrToInt64(GetData(row, 4)),
			QuestionCount:      StrToInt64(GetData(row, 5)),
			EssayQuestionCount: StrToInt64(GetData(row, 6)),
			Threshold:          int(StrToInt64(GetData(row, 7))),
			StarZone:           GetData(row, 8),
		})
	}
	return
}

// 问题导入
func importQuestion(rows [][]string) (err error) {
	for index, row := range rows {
		if index == 0 {
			continue
		}

		// 查询关卡对应的ID
		find, _ := bll.GameLevel.Find(context.Background(), &model.GameLevelInfoRequest{
			Weight:   int(StrToInt64(GetData(row, 1))),
			ParentId: int(StrToInt64(GetData(row, 0))),
		})

		if find.Id <= 0 {
			continue
		}

		// 插入关卡数据
		err = bll.Question.Create(context.Background(), &model.QuestionCreateRequest{
			Title:    GetData(row, 2),
			Type:     int(StrToInt64(GetData(row, 3))),
			Image:    GetImageUrl(GetData(row, 4)),
			AnswerId: 0,
			LevelId:  find.Id,
		})
	}
	return
}

// 选项导入
func importOption(rows [][]string) (err error) {
	for index, row := range rows {
		if index == 0 {
			continue
		}

		// 查询关卡对应的ID
		find, _ := bll.GameLevel.Find(context.Background(), &model.GameLevelInfoRequest{
			Weight:   int(StrToInt64(GetData(row, 1))),
			ParentId: int(StrToInt64(GetData(row, 0))),
		})

		if find == nil || find.Id <= 0 {
			continue
		}

		// 查询问题
		result, _ := bll.Question.Find(context.Background(), &model.QuestionInfoRequest{
			Name:    GetData(row, 2),
			LevelId: find.Id,
		})

		if result == nil || result.Id == 0 {
			continue
		}

		// 查看选项是否存在
		optionFind, _ := bll.Option.Find(context.Background(), &model.OptionInfoRequest{
			TitleId: result.Id,
			Image:   GetImageUrl(GetData(row, 4)),
			Desc:    GetData(row, 3),
		})
		if optionFind != nil && optionFind.Id > 0 {
			continue
		}

		optionData := &model.OptionCreateRequest{
			TitleId:      result.Id,
			Image:        GetImageUrl(GetData(row, 4)),
			Desc:         GetData(row, 3),
			AnalysisText: GetData(row, 6),
			AnalysisImg:  GetImageUrl(GetData(row, 7)),
		}

		// 插入关卡数据
		id, err := bll.Option.Create(context.Background(), optionData)
		if err != nil {
			fmt.Println("OptionCreateRequest:", err)
		}

		// 正确结果
		if err == nil && int(StrToInt64(GetData(row, 5))) == 1 {
			bll.Question.Update(context.Background(), &model.QuestionUpdateRequest{
				Id:       result.Id,
				AnswerId: &id,
			})
		}
	}
	return
}

func StrToInt64(value string) int64 {
	dst, err := strconv.ParseInt(value, 10, 64)
	if err != nil {
		return 0
	}
	return int64(dst)
}

func GetData(row []string, index int) string {
	if len(row) <= index {
		return ""
	}

	return row[index]
}

func GetImageUrl(image string) string {
	if image == "" {
		return ""
	}
	return fmt.Sprintf("https://shengu-quiz.oss-accelerate.aliyuncs.com/maya/%v.jpeg", image)
}
